package com.remote.server;


import com.remote.common.RemoteFactory;
import com.remote.common.command.Commands;
import com.remote.common.command.handler.ICommandHandler;
import com.remote.common.context.AbstractRemoteContext;
import com.remote.common.context.RemoteChannelInitializer;
import com.remote.common.context.listener.CloseListener;
import com.remote.common.context.listener.ConnectListener;
import com.remote.common.context.listener.ReadListener;
import com.remote.common.exception.RemoteException;
import com.remote.common.model.Request;
import com.remote.common.model.Response;
import com.remote.server.handle.*;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;


public class RemoteServer extends AbstractRemoteContext {
    private static final Logger LOGGER = LoggerFactory.getLogger(RemoteServer.class);

    private NioEventLoopGroup boss;
    private NioEventLoopGroup worker;
    private ServerBootstrap bootstrap;

    public RemoteServer(RemoteFactory remoteFactory) {
        super(remoteFactory);
        this.getRemoteFactory().registerHandler(Commands.CONNECT, new ConnectHandler(this));
        this.getRemoteFactory().registerHandler(Commands.CONTROL, new ControlHandle(this));
        this.getRemoteFactory().registerHandler(Commands.HEARTBEAT, new HeartBeatHandler(this));
        this.getRemoteFactory().registerHandler(Commands.SCREEN, new ScreenCommandHandler(this));
        this.getRemoteFactory().registerHandler(Commands.KEYBOARD, new FireCommandHandler(this));
        this.getRemoteFactory().registerHandler(Commands.MOUSE, new FireCommandHandler(this));
        this.getRemoteFactory().registerHandler(Commands.QUALITY, new FireCommandHandler(this));
        this.getRemoteFactory().registerHandler(Commands.TERMINATE, new FireCommandHandler(this));
        this.getRemoteFactory().registerHandler(Commands.SEND_MESSAGE, new FireCommandHandler(this));
        this.getRemoteFactory().registerHandler(Commands.MODIFY_DPI, new FireCommandHandler(this));
    }


    @Override
    protected void initBootstrap() {
        if (getEncoderClass() == null) {
            this.setEncoderClass(Response.class);
        }
        if (getDecoderClass() == null) {
            this.setDecoderClass(Request.class);
        }
        if (getChannelHandler() == null) {
            this.setChannelHandler(new ServerChannelHandler(new ReadListener<Request>() {
                @Override
                public void onRead(ChannelHandlerContext ctx, Request obj) {
                    final ICommandHandler commandHandler = getRemoteFactory().getCommandHandler(obj.getCommand());
                    commandHandler.handle(ctx, obj);
                }
            }));
        }
        if (getChannelInitializer() == null) {
            this.setChannelInitializer(new RemoteChannelInitializer());
        }
        Map<ChannelOption, Object> options = this.getRemoteFactory().getRemoteConfiguration().getOptions();
        //服务端配置
        this.boss = new NioEventLoopGroup();
        this.worker = new NioEventLoopGroup();
        this.bootstrap = new ServerBootstrap();
        this.bootstrap.group(boss, worker);
        this.bootstrap.channel(NioServerSocketChannel.class);
        this.bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
        this.bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
        this.bootstrap.childHandler(this.getChannelInitializer());
        for (Map.Entry<ChannelOption, Object> channelOptionObjectEntry : options.entrySet()) {
            this.bootstrap.childOption(channelOptionObjectEntry.getKey(), channelOptionObjectEntry.getValue());
        }
    }

    @Override
    public Channel onConnect() throws RemoteException {
        try {
            //查找配置
            String host = this.getRemoteFactory().getRemoteConfiguration().getHost();
            int port = this.getRemoteFactory().getRemoteConfiguration().getPort();
            //服务端启动
            final ChannelFuture channelFuture = bootstrap.bind(host, port).sync();
            LOGGER.info("server start on port:{}", port);
            Channel channel = channelFuture.channel();

            channelFuture.addListeners().addListeners(new GenericFutureListener<Future<? super Void>>() {
                @Override
                public void operationComplete(Future<? super Void> future) throws Exception {
                    for (ConnectListener connectListener : getConnectListeners()) {
                        connectListener.onConnect(channel);
                    }
                }
            });
            channel.closeFuture().addListeners(new GenericFutureListener<Future<? super Void>>() {
                @Override
                public void operationComplete(Future<? super Void> future) throws Exception {
                    for (CloseListener closeListener : getCloseListeners()) {
                        closeListener.onClose(channel);
                    }
                }
            });
            channel.closeFuture().sync();
            return channel;
        } catch (InterruptedException e) {
            throw new RemoteException(e);
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }


    @Override
    public void close() {
    }

    @Override
    public void refresh() {

    }
}
